package com.molichuxing.services.infrastructure.service.impl;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.molichuxing.services.business.dto.request.create.PaymentBizCreateDto;
import com.molichuxing.services.infrastructure.bean.EscrowLogBean;
import com.molichuxing.services.infrastructure.bean.EscrowNotifyLogBean;
import com.molichuxing.services.infrastructure.dao.EscrowLogMapper;
import com.molichuxing.services.infrastructure.dao.EscrowNotifyLogMapper;
import com.molichuxing.services.infrastructure.dto.response.PrepaymentDto;
import com.molichuxing.services.infrastructure.dto.response.UnionpayResultDto;
import com.molichuxing.services.infrastructure.service.UnionpayService;
import com.molichuxing.services.property.EscrowLogTypeEnum;
import com.molichuxing.services.property.EscrowNotifyLogTypeEnum;
import com.molichuxing.services.property.PaymentChannelEnum;
import com.molichuxing.services.property.sdk.AlipayTradeStateEnum;
import com.molichuxing.services.sdk.union.AcpService;
import com.molichuxing.services.utils.UnionpayUtil;

@Service("unionpayService")
public class UnionpayServiceImpl implements UnionpayService {
	private static final Logger logger = org.apache.log4j.Logger.getLogger(UnionpayServiceImpl.class);
	@Resource
	private EscrowLogMapper escrowLogMapper;

	@Resource
	private EscrowNotifyLogMapper escrowNotifyLogMapper;

	/**
	 * 统一下单(预支付单)
	 * 
	 * @return
	 * @throws Exception
	 */
	@Override
	public PrepaymentDto orderPay(PaymentBizCreateDto createDto) throws Exception {
		logger.info("银联生成预支付单  入参："+ JSON.toJSONString(createDto));
		PrepaymentDto resultJson = UnionpayUtil.createOrderStr(String.valueOf(createDto.getPayAmount()), null);
		logger.info("银联生成预支付单  出参："+ JSON.toJSONString(resultJson));
		return resultJson;
	}

	@Override
	public UnionpayResultDto notifyPay(Map<String, String> reqParam) throws Exception {
		UnionpayResultDto result = null;
		try {
			logger.info("BackRcvResponse接收后台通知开始");
			// 获取银联通知服务器发送的后台通知参数
			logger.info("打印返回的参数："+reqParam);
			//重要！验证签名前不要修改reqParam中的键值对的内容，否则会验签不过
			if (!AcpService.validate(reqParam, "UTF-8")) {
				logger.info("验证签名结果[失败].");
				//验签失败，需解决验签问题
			} else {
				logger.info("验证签名结果[成功].");
				//【注：为了安全验签成功才应该写商户的成功处理逻辑】交易成功，更新商户订单状态
				String outTradeNo = reqParam.get("orderId"); //获取后台通知的数据，其他字段也可用类似方式获取
				String respCode = reqParam.get("respCode");
				String queryId = reqParam.get("queryId");	//银联流水号
				String txnType = reqParam.get("txnType");	//原交易的txnType
				String txnSubType = reqParam.get("txnSubType");	//一般情况同原交易的txnSubType。当txnType为01时，如果txnSubType为03，表示这笔交易为分期交易。
				String settleDate = reqParam.get("settleDate");	//清算日期
				String settleCurrencyCode = reqParam.get("settleCurrencyCode");	//清算币种。境内商户固定返回156
				String settleAmt = reqParam.get("settleAmt");	//清算金额。境内商户同原交易的txnAmt。
				String traceTime = reqParam.get("traceTime");	//交易传输时间
				String txnTime = reqParam.get("txnTime");	//交易传输时间
				String traceNo = reqParam.get("traceNo");	//系统跟踪号。收单机构对账时使用，该域由银联系统产生。
				//判断respCode=00、A6后，对涉及资金类的交易，请再发起查询接口查询，确定交易成功后更新数据库。
				logger.info("银联回调的状态码。respCode："+JSON.toJSONString(respCode));
				if((respCode.equals("00"))||(respCode.equals("A6"))) {
					// 生成异步通知信息
					result = new UnionpayResultDto();
					result.setTotalAmount(new BigDecimal(settleAmt));
					result.setOutTradeNo(outTradeNo);
					result.setGmtPayment(LocalDateTime.now());
					result.setTradeNo(queryId);
					result.setTradeState(AlipayTradeStateEnum.TRADE_SUCCESS);
					result.setTradeNo(traceNo);
				}
			}
			logger.info("BackRcvResponse接收后台通知结束");
		} catch (Exception e) {
			logger.error("支付结果通知异常", e);
		}
		return result;
	}

	/**
	 * 保存请求日志
	 * 
	 * @param reqData
	 * @param respData
	 */
	@Transactional(propagation = Propagation.NOT_SUPPORTED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
	public void saveEscrowLog(String payNumber, AlipayTradeAppPayRequest reqData, AlipayTradeAppPayResponse respData,
			EscrowLogTypeEnum type) {
		EscrowLogBean escrowLogBean = new EscrowLogBean();
		AlipayTradeAppPayModel payModel = (AlipayTradeAppPayModel) reqData.getBizModel();
		escrowLogBean.setPayNumber(payModel.getOutTradeNo());
		escrowLogBean.setRequestData(JSONObject.toJSONString(reqData));
		escrowLogBean.setResponseData(JSONObject.toJSONString(respData));
		escrowLogBean.setChannel(PaymentChannelEnum.UNIONPAY.getValue());
		escrowLogBean.setType(type.getValue());
		escrowLogBean.setRemark(type.getName());
		escrowLogMapper.create(escrowLogBean);
	}

	/**
	 * 保存回调日志
	 * 
	 * @param notifyData
	 * @param type
	 */
	@Transactional(propagation = Propagation.NOT_SUPPORTED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
	public void saveNotifyLog(String payNumber, Map<String, String> notifyData, EscrowNotifyLogTypeEnum type) {
		try {
			EscrowNotifyLogBean escrowNotifyLogBean = new EscrowNotifyLogBean();
			escrowNotifyLogBean.setPayNumber(payNumber);
			escrowNotifyLogBean.setChannel(PaymentChannelEnum.UNIONPAY.getValue());
			escrowNotifyLogBean.setType(type.getValue());
			escrowNotifyLogBean.setNotifyData(JSONObject.toJSONString(notifyData));
			escrowNotifyLogMapper.create(escrowNotifyLogBean);
		} catch (Exception e) {
			logger.error("[saveNotifyLog]银联({}:{})回调保存日志异常");
		}
	}
}
